﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Input = Microsoft.Xna.Framework.Input; // to provide shorthand to clear up ambiguities

namespace IXBoxJoystickSerial
{
    public partial class Form1 : Form
    {
        //Joystick Variables
        GamePadState gamePadState;
        GamePadState previousState;

        static int m_iNbDataLoad = 3;                       //Number of joystick state element to send
        Byte[] m_JoysickStateBuffer = new Byte[m_iNbDataLoad];    //Buffer that contains the joystick's state
        Boolean m_bIsSedingBufferDone = false;

        //Communication Protocol Variables
        Boolean m_bSendToSerial;                            //If we are allowed to send to serial
        Boolean m_bIsSerialConfigured;                      //If the serial is configured and ready to send

        static int m_iBufferSize = m_iNbDataLoad + 3;       //0:PuissanceMoteur, 1:Profondeur, 2:Direction
        Byte[] m_BufferToSend = new Byte[m_iBufferSize];  //The buffer to send to the serial   
        Byte[] m_ReceivedBuffer = new Byte[m_iBufferSize];//The buffer to send to the serial
        Boolean m_bIsEchoActive = false;                            //Receive what we send
        Boolean m_bReceptionBufferReady;
        Boolean m_bIsLatencyTestIsRunning = false;

        static Byte m_iSeparationByte = 255;

        Boolean m_bIsEasyStarON = false;  

        //Joystick Setting
        static Byte m_iDeriveMaxDefault = 171;
        static Byte m_iDeriveMoyDefault = 108;
        static Byte m_iDeriveMinDefault = 36;

        static Byte m_iProfondeurMaxDefault = 171;
        static Byte m_iProfondeurMoyDefault = 108;
        static Byte m_iProfondeurMinDefault = 36;

        Byte m_iDeriveMax = m_iDeriveMaxDefault;
        Byte m_iDeriveMoy = m_iDeriveMoyDefault;
        Byte m_iDeriveMin = m_iDeriveMinDefault;

        Byte m_iProfondeurMax = m_iProfondeurMaxDefault;
        Byte m_iProfondeurMoy = m_iDeriveMoyDefault;
        Byte m_iProfondeurMin = m_iDeriveMinDefault;

        Boolean m_bIsControllerConfigured = false;

        //Latency time test
        static Byte[] m_LatencyTestBuffer = { 255, 1, 0, m_iDeriveMoyDefault, m_iProfondeurMoyDefault, 181 };
        DateTime m_tLatencyInstant = new DateTime();
        long m_iLatencyTime = 0;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            /************************************************************/
            /*                 Serial Communication Protocol            */
            /************************************************************/
            //The Serial Configuration Zone is Enabled only when the check box is checked.

            //Link the serial port to a method:
            SerialPortSelected.DataReceived += new SerialDataReceivedEventHandler(DataReceviedHandler);

            //Disable the OK button
            btnSerialConfOK.Enabled = false;

            //Disable the Latency Test button
            btnLattencyTest.Enabled = false;

            //Is Serial Configured
            m_bIsSerialConfigured = false;

            //Disable sending to Serial
            m_bSendToSerial = false;

            //Disable the Echo
            m_bIsEchoActive = false;
            
            //The Reception buffer isn't ready
            m_bReceptionBufferReady = false;

            //Disable the list of combobox
            comboBoxListCOM.Enabled = false;
            /************************************************************/

            //EasyStar:
            m_JoysickStateBuffer[1] = m_iDeriveMoyDefault;//Value to send to servo Dérive
            m_JoysickStateBuffer[2] = m_iProfondeurMoyDefault;//Value to send to servo Profondeur


            /************************************************************/
            /**************Joystick**************************************/
            numProfondeurMin.Enabled = false;
            numProfondeurMoy.Enabled = false;
            numProfondeurMax.Enabled = false;
            numDeriveMin.Enabled = false;
            numDeriveMoy.Enabled = false;
            numDeriveMax.Enabled = false;

            //
            buttonConfigControllerDefault.Enabled = false;

            //If the controller is configured:
            m_bIsControllerConfigured = true;
            /************************************************************/
        }

        //On Timer Tick
        private void StateTimer_Tick(object sender, EventArgs e)
        {
            UpdateSerialConfigurationVisual();

            UpdateJoystickState();

            UpdateEasyStarVisualState();

            SendControlToSerial();
        }

        //----------------Serial Port Methods-------------------------//
        private void UpdateListOfCOMPort()
        {
            comboBoxListCOM.Items.Clear();
            for (int i = 0; i < SerialPort.GetPortNames().Length; i++)
            {
                comboBoxListCOM.Items.Add(SerialPort.GetPortNames()[i]);
            }
        }

        //On reception of data from the serial port:
        private void DataReceviedHandler(object sender,SerialDataReceivedEventArgs e)
        {

            //Build the reception buffer:
            if (SerialPortSelected.BytesToRead >= m_iBufferSize)
            {
                //Check if the received byte is beginning byte:
                SerialPortSelected.Read(m_ReceivedBuffer, 0, 1);

                if (m_ReceivedBuffer[0] == m_iSeparationByte)
                {
                    //The reception buffer isn't build yet
                    m_bReceptionBufferReady = false;

                    //Read the received data:
                    SerialPortSelected.Read(m_ReceivedBuffer, 1, m_iBufferSize-1);

                    //Check that the buffer is correct to send
                    m_bReceptionBufferReady = CheckBufferOK(m_ReceivedBuffer);

                    //Check if it is an echo message:
                    if (m_ReceivedBuffer[1] == 1)
                    {
                        m_bIsEchoActive = false;
                    }
                    
                    //Check if it is a latency time:
                    if (m_bIsLatencyTestIsRunning)
                    {
                        //System.Threading.Thread.Sleep(1000);
                        m_iLatencyTime = (DateTime.Now.Ticks - m_tLatencyInstant.Ticks)/10000; //1ms = 10 000ticks
                        m_bIsLatencyTestIsRunning = false;
                    }

                    
                }
            }

        }

        //Check that the buffer is coherent:
        private Boolean CheckBufferOK(Byte[] bBuffer)
        {
            return true; //for test

            //
            if (bBuffer.Length != m_iBufferSize)
                return false;

            //Check Sum Control:
            Byte iCheckSumComputed = 0;
            for (int i = 0; i < bBuffer.Length-1; i++)
            {
                iCheckSumComputed += bBuffer[i];
            }

            //When the checksumm is equal to m_iSeparationByte we set it to 0
            if (iCheckSumComputed == m_iSeparationByte)
                iCheckSumComputed = 0;
            
            if (iCheckSumComputed != bBuffer[bBuffer.Length - 1])
                return false;

            return true;
        }

        //Update visuals related to the serial configuration
        private void UpdateSerialConfigurationVisual()
        {
            if (m_bIsSerialConfigured)
            {
                labelSerialConfiguration.Text = "Configured to " + SerialPortSelected.PortName;
                labelSerialConfiguration.BackColor = Color.GreenYellow;
            }
            else
            {
                labelSerialConfiguration.Text = "Not Configured";
                labelSerialConfiguration.BackColor = Color.Red;
            }
        }

        //On Serial Port configuration OK button click
        private void button1_Click(object sender, EventArgs e)
        {
            //Get the entered serial port
            String sSelectedPort;
            sSelectedPort = (String) comboBoxListCOM.SelectedItem ;

            Boolean bIsSelectedPortPresent = false;

            //Check if there is a port COM available
            int iNumberOfPorts = SerialPort.GetPortNames().Length;
            if (iNumberOfPorts == 0)
            {
                MessageBox.Show("Error: Laysa Honâka Ayyo Port COM Fî Uâsôbik!");
            }

            //Get the list of serial ports
            String sListSerialPortNames = "";

            String[] sSerialPortnames = new String[iNumberOfPorts];
            sSerialPortnames = SerialPort.GetPortNames();
            for (int i = 0; i < iNumberOfPorts; i++)
            {
                sListSerialPortNames += sSerialPortnames[i] + ",";
                
                //Check if the selected port is present
                bIsSelectedPortPresent |= sSelectedPort == sSerialPortnames[i];
            }

            if (!bIsSelectedPortPresent)
            {
                MessageBox.Show("Error: Al qima l'moütâra gayro câliua, àiütar wâuidatan min honâ:" + sListSerialPortNames);
            }
            else
            {
                //Set the value of the serial:
                SerialPortSelected.Close();
                SerialPortSelected.PortName = sSelectedPort;

                //Open the serial:
                try
                {
                    SerialPortSelected.Open();
                }
                catch (Exception ey)
                {
                    MessageBox.Show("Error: Honâka üalalon mâ fi l'Port COM. Iütar qîmatan àoürâ.");
                    return;
                }

                //Now the serial is configured and ready to work:
                m_bIsSerialConfigured = true;

                //We exit the configuration:
                chckBoxSerialConf.Checked = false;
                checkBox1_CheckedChanged(this, e);
            }            
        }

        //Enable or Disable serial port configuration
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (chckBoxSerialConf.Checked)
            {
                //Enable the OK button
                btnSerialConfOK.Enabled = true;

                //Enable the COM Port combo box
                comboBoxListCOM.Enabled = true;

                //The Serial isn't configured
                m_bIsSerialConfigured = false;

                //Close the serial:
                SerialPortSelected.Close();

                //Update the list of com port:
                UpdateListOfCOMPort();
            }
            else
            {
                //Disable the OK button
                btnSerialConfOK.Enabled = false;

                //Disable the COM Port combo box
                comboBoxListCOM.Enabled = false;
            }
        }

        private void SendControlToSerial()
        {
            
            //We prepare to send buffer:
            m_bIsSedingBufferDone = false;

            //Build the buffer to send:
            ////1:Separation:
            m_BufferToSend[0] = m_iSeparationByte;
            
            ////2:Type of command
            if (m_bIsEchoActive)
                m_BufferToSend[1] = 1;  //Sending with Echo
            else
                m_BufferToSend[1] = 0;  //Sending without echo

            ////Controller State buffer:
            for (int i = 0; i < m_JoysickStateBuffer.Length; i++)
            {
                m_BufferToSend[2 + i] = m_JoysickStateBuffer[i];
            }

            //Check that the serial is ready and send
            if (m_bSendToSerial && m_bIsSerialConfigured && m_bIsControllerConfigured)
            {
                SerialPortSelected.Write(m_BufferToSend, 0, m_BufferToSend.Length);
                m_bIsSedingBufferDone = true;
            }
        }

        //--------------------------XBox Joystick Methods----------//
        
        //
        private void UpdateJoystickState()
        {
            //Get the new gamepad state and save the old state.
            this.previousState = this.gamePadState;
            this.gamePadState = GamePad.GetState(PlayerIndex.One);

            //Check if the joystick is connected:
            if (!gamePadState.IsConnected)
            {
                labelJoystickConnected.Text = "Joystick Not Connected";
                labelJoystickConnected.BackColor = Color.Red;
                return;
            }
            else
            {
                labelJoystickConnected.Text = "Joystick Connected";
                labelJoystickConnected.BackColor = Color.GreenYellow;
            }

            //m_JoysickStateBuffer->0:PuissanceMoteur, 1:Profondeur, 2:Direction

            //Update the Joystick state when it changed
            ////The buttons
            if (!this.gamePadState.Buttons.Equals(this.previousState.Buttons))
            {
                //Enable Disable Sending:
                if (this.gamePadState.Buttons.Start == Input.ButtonState.Pressed)
                {
                    m_bSendToSerial = !m_bSendToSerial && m_bIsSerialConfigured && m_bIsControllerConfigured;
                    if (m_bSendToSerial)
                    {
                        labelSendingToSerial.Text = "Sending";
                        labelSendingToSerial.BackColor = Color.GreenYellow;
                        //We don't allow configuration when we are sending
                        chckBoxSerialConf.Enabled = false;
                        //We don't allow latency test when sending
                        btnLattencyTest.Enabled = false;
                        //We don't allow controller configuration when sending
                        checkBoxControllerConf.Enabled = false;
                    }
                    else
                    {
                        labelSendingToSerial.Text = "Not Sending";
                        labelSendingToSerial.BackColor = Color.Red;
                        chckBoxSerialConf.Enabled = true;
                        btnLattencyTest.Enabled = true;
                        checkBoxControllerConf.Enabled = true;
                    }
                }
            }

            ////ThumbSticks
            if (!this.gamePadState.ThumbSticks.Equals(this.previousState.ThumbSticks))
            {
                //this.x1Position.Value = (int)((this.gamePadState.ThumbSticks.Left.X + 1.0f) * 100.0f / 2.0f);
                //this.y2position.Value = (int)((this.gamePadState.ThumbSticks.Right.Y + 1.0f) * 100.0f / 2.0f);

                //Profondeur:
                Byte iProfondeur = (Byte)((this.gamePadState.ThumbSticks.Right.Y + 1.0f) * 100.0f / 2.0f); ;
                m_JoysickStateBuffer[2] = (Byte)((float)iProfondeur * 180.0f / 100.0f);
                pbarProfondeur.Value = (int)iProfondeur;
                labelProfondeur.Text = iProfondeur.ToString();
                
                //Dérive
                Byte iDerive = (Byte)((this.gamePadState.ThumbSticks.Left.X + 1.0f) * 100.0f / 2.0f);
                 m_JoysickStateBuffer[1] = (Byte)((float)iDerive*180.0f/100.0f);
                pbarDerive.Value = (int)iDerive;
                labelDerive.Text = iDerive.ToString();
                
                //Apdate the values of the Joystick State buffer according to the Joystick configuration
                UpdateJoystickStateBuffer();
                
            }

            ////Triggers
            if (!this.gamePadState.Triggers.Equals(this.previousState.Triggers))
            {
                //this.leftTriggerPosition.Value = (int)((this.gamePadState.Triggers.Left * 100));
                //Puissance Moteur
                Byte iPuissanceMoteur = (Byte)(this.gamePadState.Triggers.Right * 100);
                m_JoysickStateBuffer[0] = (Byte)((float)iPuissanceMoteur * 180.0f / 100.0f); ;
                pbarPuissanceMoteur.Value = (int)iPuissanceMoteur;
                labelPuissanceMoteur.Text = iPuissanceMoteur.ToString();
            }
        }
        
        //Update the values of the joystick state buffer according to the joystick configuration:
        private void UpdateJoystickStateBuffer()
        {
            //m_JoysickStateBuffer[1] dérive
            //m_JoysickStateBuffer[2] profondeur

            Byte iDerive = m_JoysickStateBuffer[1];
            Byte iProfondeur = m_JoysickStateBuffer[2];

            if (iDerive >= 90)
            {
                m_JoysickStateBuffer[1] = (Byte)((((float)iDerive) * ((float)m_iDeriveMax - (float)m_iDeriveMoy) / 90.0f) + (float)m_iDeriveMoy * 2.0f - (float)m_iDeriveMax);
            }
            else if (iDerive < 90)
            {
                m_JoysickStateBuffer[1] = (Byte)((((float)iDerive) * ((float)m_iDeriveMoy - (float)m_iDeriveMin) / 90.0f) + (float)m_iDeriveMin);
            }

            if (iProfondeur >= 90)
            {
                m_JoysickStateBuffer[2] = (Byte)((((float)iProfondeur) * ((float)m_iProfondeurMax - (float)m_iProfondeurMoy) / 90.0f) + (float)m_iProfondeurMoy * 2.0f - (float)m_iProfondeurMax);
            }
            else if (iProfondeur < 90)
            {
                m_JoysickStateBuffer[2] = (Byte)((((float)iProfondeur) * ((float)m_iProfondeurMoy - (float)m_iProfondeurMin) / 90.0f) + (float)m_iProfondeurMin);
            }

        }

        //Update the state of the EasyStar one the form:
        private void UpdateEasyStarVisualState()
        {
            //Get the state of the EasyStar from the buffer

            //Update the state of the plane on the display
            ////The power of easy Star
            if (m_bIsEasyStarON)
            {
                EasyStar_Power.Text = "ON";
                EasyStar_Power.BackColor = Color.GreenYellow;
            }
            else
            {
                EasyStar_Power.Text = "OFF";
                EasyStar_Power.BackColor = Color.Red;
            }

            ////Message received/sent:
            UpdateMsgReceivedSend();

        }

        //Update the last received and sent message values:
        private void UpdateMsgReceivedSend(){
            
            //Update the received value on the visual
            if(m_bReceptionBufferReady)
            {
                string outdata = "";
                for (int i = 0; i < this.m_ReceivedBuffer.Length; i++)
                {
                    int iReceived = (int)(m_ReceivedBuffer[i]);
                    //sBuffer += "["+sReceived[i].ToString()+"]";
                    outdata += "[" + iReceived.ToString() + "] ";
                }

                this.LablLastReceivedMsg.Text = outdata;
            }

            if (m_bIsSedingBufferDone)
            {
                string outdata = "";
                for (int i = 0; i < this.m_BufferToSend.Length; i++)
                {
                    int iReceived = (int)(m_BufferToSend[i]);
                    //sBuffer += "["+sReceived[i].ToString()+"]";
                    outdata += "[" + iReceived.ToString() + "] ";
                }

                this.LablLastSentMsg.Text = outdata;
            }
        }

        private void btnLattencyTest_Click(object sender, EventArgs e)
        {
            if (m_bIsSerialConfigured && m_bIsControllerConfigured)
            {
                //Prepare the latency test:
                m_bIsLatencyTestIsRunning = true;
                m_tLatencyInstant = DateTime.Now;
                m_iLatencyTime = 0;
                //Send a message:
                SerialPortSelected.Write(m_LatencyTestBuffer, 0, m_LatencyTestBuffer.Length);
            }
            else
            {
                MessageBox.Show("l'Port COM laysa mo3adälan!");
            }
        }

        private void DisplayTimer_Tick(object sender, EventArgs e)
        {
            //Update the value of the latency time on the screen:
            if (m_bIsLatencyTestIsRunning ==false)
            {
                labelLatencyValue.Text = m_iLatencyTime.ToString() + "ms";
                //m_bIsLatencyTestIsRunning = false;
            }

            if (m_bIsSerialConfigured && m_bIsControllerConfigured && !m_bSendToSerial)
            {
                btnLattencyTest.Enabled = true;
            }
            else
            {
                btnLattencyTest.Enabled = false;
            }
        }

        private void EasyStarTimer_Tick(object sender, EventArgs e)
        {
            //Echo active means that we requested a return back msg without acknowlegement
            if (!m_bIsEchoActive)
            {
                m_bIsEasyStarON = true;
                m_bIsEchoActive = true;
            }
            else
            {
                m_BufferToSend[1] = 0;

                //When we don't receive the echo this means the easystar didn't send back the message
                m_bIsEasyStarON = false;
            }
        }

        private void numProfondeurMin_ValueChanged(object sender, EventArgs e)
        {
            m_iProfondeurMin = (Byte) numProfondeurMin.Value;
        }

        private void numProfondeurMoy_ValueChanged(object sender, EventArgs e)
        {
            m_iProfondeurMoy = (Byte)numProfondeurMoy.Value;
        }

        private void numProfondeurMax_ValueChanged(object sender, EventArgs e)
        {
            m_iProfondeurMax = (Byte)numProfondeurMax.Value;
        }

        private void numDeriveMin_ValueChanged(object sender, EventArgs e)
        {
            m_iDeriveMin = (Byte)numDeriveMin.Value;
        }

        private void numDeriveMoy_ValueChanged(object sender, EventArgs e)
        {
            m_iDeriveMoy = (Byte)numDeriveMoy.Value;
        }

        private void numDeriveMax_ValueChanged(object sender, EventArgs e)
        {
            m_iDeriveMax = (Byte)numDeriveMax.Value; 
        }

        private void checkBoxControllerConf_CheckedChanged(object sender, EventArgs e)
        {
            //We don't allow the controller configuration when the serial is sending
            if (m_bSendToSerial)
            {
                checkBoxControllerConf.Checked = false;
                return;
            }
            if (checkBoxControllerConf.Checked )
            {
                numProfondeurMin.Enabled = true;
                numProfondeurMoy.Enabled = true;
                numProfondeurMax.Enabled = true;
                numDeriveMin.Enabled = true;
                numDeriveMoy.Enabled = true;
                numDeriveMax.Enabled = true;

                //If the controller is configured:
                m_bIsControllerConfigured = false;

                //
                buttonConfigControllerDefault.Enabled = true;
            }
            else
            {
                numProfondeurMin.Enabled = false;
                numProfondeurMoy.Enabled = false;
                numProfondeurMax.Enabled = false;
                numDeriveMin.Enabled = false;
                numDeriveMoy.Enabled = false;
                numDeriveMax.Enabled = false;

                //Set that the controller is configured:
                m_bIsControllerConfigured = true;

                //
                buttonConfigControllerDefault.Enabled = false;
            }
        }

        private void buttonConfigControllerDefault_Click_1(object sender, EventArgs e)
        {
            m_iDeriveMin        = m_iDeriveMinDefault;
            numDeriveMin.Value  = m_iDeriveMinDefault;

            m_iDeriveMoy        = m_iDeriveMoyDefault;
            numDeriveMoy.Value  = m_iDeriveMoyDefault;

            m_iDeriveMax        = m_iDeriveMaxDefault;
            numDeriveMax.Value  = m_iDeriveMaxDefault;
                   
            m_iProfondeurMax        = m_iProfondeurMaxDefault;
            numProfondeurMax.Value  = m_iProfondeurMaxDefault;

            m_iProfondeurMoy        = m_iProfondeurMoyDefault;
            numProfondeurMoy.Value  = m_iProfondeurMoyDefault;

            m_iProfondeurMin        = m_iProfondeurMinDefault;
            numProfondeurMin.Value = m_iProfondeurMinDefault;

        }
 
     }
}
